package com.happok.onlineclass.usermanagement.user.service;

import cn.hutool.core.bean.BeanUtil;
import com.happok.common.exception.BusinessException;
import com.happok.common.exception.NotFoundException;
import com.happok.common.exception.RepeatException;
import com.happok.onlineclass.currentuser.CurrentUser;
import com.happok.onlineclass.currentuser.CurrentUserResolver;
import com.happok.onlineclass.usermanagement.authentication.dto.LoginResultDTO;
import com.happok.onlineclass.usermanagement.authentication.service.AuthenticationService;
import com.happok.onlineclass.usermanagement.common.dto.Contants;
import com.happok.onlineclass.usermanagement.common.dto.IdDTO;
import com.happok.onlineclass.usermanagement.common.exception.PreconditionFailedException;
import com.happok.onlineclass.usermanagement.common.exception.UserDisableException;
import com.happok.onlineclass.usermanagement.feign.service.CustomFormService;
import com.happok.onlineclass.usermanagement.setting.dto.FunctionClassifyEnum;
import com.happok.onlineclass.usermanagement.setting.service.SettingService;
import com.happok.onlineclass.usermanagement.user.dataobject.UserDO;
import com.happok.onlineclass.usermanagement.user.dto.*;
import com.happok.onlineclass.usermanagement.verifycode.service.VerifyCodeService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.net.URISyntaxException;
import java.util.Map;

import static org.springframework.security.web.context.HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY;

/**
 * 接口分类                        yapi 分类文件夹
 * 功能说明
 *
 * @author : chenac
 * @module 项目名称/模块名          yapi 项目组/子项目
 * @date : 2021/7/15 14:47
 */
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDaoService userDaoService;

    @Autowired
    private ManageUserService manageUserService;

    @Autowired
    private CustomFormService customFormService;

    @Autowired
    private VerifyCodeService verifyCodeService;

    @Autowired
    private SettingService settingService;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private AuthenticationManager authManager;

    @Autowired
    private AuthenticationService authenticationService;

    @Override
    public void updatePersonalUser(String user, UpdateUserDTO updateUserDTO) throws Exception {
        CurrentUser currentUser = CurrentUserResolver.getCurrentUser(user);
        ManageUpdateUserDTO manageUpdateUserDTO = new ManageUpdateUserDTO();
        BeanUtil.copyProperties(updateUserDTO, manageUpdateUserDTO);
        manageUserService.updateUserByUserId(user, currentUser.getId(), manageUpdateUserDTO);
    }

    @Override
    public UserDetailDTO getPersonalUserDetail(String user) throws URISyntaxException {
        CurrentUser currentUser = CurrentUserResolver.getCurrentUser(user);
        userDaoService.isUserExist(currentUser.getId());
        UserDO userDO = userDaoService.getUserDetailById(currentUser.getId());
        UserDetailDTO userDetailDTO = new UserDetailDTO();
        BeanUtil.copyProperties(userDO, userDetailDTO);
        userDetailDTO.setForcePwdChange(userDO.getForcePassword());
        if (!ObjectUtils.isEmpty(userDO.getExtendInfoId())) {
            Map<String, Object> extendData = customFormService.getFormRecord(userDO.getExtendInfoId(), Contants.HTTP_CUSTOM_FORM_USER_INFO);
            userDetailDTO.setExtendData(extendData);
        }
        return userDetailDTO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public LoginResultDTO registerUser(RegisterUserDTO registerUserDTO, HttpServletRequest req) throws URISyntaxException {
        if (ObjectUtils.isEmpty(registerUserDTO.getPhone()) &&
                ObjectUtils.isEmpty(registerUserDTO.getEmail())) {
            throw new BusinessException("参数异常");
        }
        // 判断注册功能是否开启
        settingService.checkFunctionAndSetting(registerUserDTO.getPhone(), registerUserDTO.getEmail(), FunctionClassifyEnum.USER_REGIST);
        // 校验验证码
        String account = !ObjectUtils.isEmpty(registerUserDTO.getPhone()) ? registerUserDTO.getPhone() : registerUserDTO.getEmail();
        verifyCodeService.checkVerifyCode(account, FunctionClassifyEnum.USER_REGIST.getFunction(), registerUserDTO.getVerifyCode());
        // 添加用户
        UserDO userDO = new UserDO();
        BeanUtils.copyProperties(registerUserDTO, userDO);
        userDaoService.addUser(userDO);
        if (!ObjectUtils.isEmpty(registerUserDTO.getExtendData())) {
            String recordId = customFormService.addFormRecord(userDO.getId(), Contants.HTTP_CUSTOM_FORM_USER_INFO, registerUserDTO.getExtendData());
            UserDO updatedUserDO = new UserDO();
            updatedUserDO.setId(userDO.getId());
            updatedUserDO.setExtendInfoId(recordId);
            userDaoService.updateUserById(updatedUserDO);
        }
        // 登录
        String userAccount = !ObjectUtils.isEmpty(registerUserDTO.getPhone()) ? registerUserDTO.getPhone() : registerUserDTO.getEmail();
        UsernamePasswordAuthenticationToken authReq
                = new UsernamePasswordAuthenticationToken(userAccount, registerUserDTO.getPassword());
        Authentication auth = authManager.authenticate(authReq);
        SecurityContext sc = SecurityContextHolder.getContext();
        sc.setAuthentication(auth);
        HttpSession session = req.getSession(true);
        session.setAttribute(SPRING_SECURITY_CONTEXT_KEY, sc);
        return authenticationService.loginAccount(userAccount);
    }

    @Override
    public IdDTO personalResetPwd(PersonalResetPwdDTO personalResetPwdDTO) {
        if (ObjectUtils.isEmpty(personalResetPwdDTO.getPhone()) &&
                ObjectUtils.isEmpty(personalResetPwdDTO.getEmail())) {
            throw new BusinessException("参数异常");
        }
        if (!personalResetPwdDTO.getNewPassword().equals(personalResetPwdDTO.getReNewPassword())) {
            throw new BusinessException("参数异常");
        }
        String account = !ObjectUtils.isEmpty(personalResetPwdDTO.getEmail()) ? personalResetPwdDTO.getEmail() : personalResetPwdDTO.getPhone();

        UserDO user = userDaoService.getUserByUsername(account);
        if (ObjectUtils.isEmpty(user)) {
            throw new NotFoundException("账号不存在");
        }
        if (!user.getEnable()) {
            throw new UserDisableException(4, "账户被禁用");
        }

        // 判断忘记密码功能是否开启
        settingService.checkFunctionAndSetting(personalResetPwdDTO.getPhone(), personalResetPwdDTO.getEmail(), FunctionClassifyEnum.FORGET_PASSWORD);
        // 校验验证码
        verifyCodeService.checkVerifyCode(account, FunctionClassifyEnum.FORGET_PASSWORD.getFunction(), personalResetPwdDTO.getVerifyCode());
        // 更新密码
        UserDO updateUser = new UserDO();
        updateUser.setId(user.getId());
        updateUser.setPassword(personalResetPwdDTO.getNewPassword());
        userDaoService.updateUserById(updateUser);
        return new IdDTO(user.getId());
    }

    @Override
    public void updateUserAccount(String user, UpdateUserAccountDTO updateUserAccountDTO) {
        if (ObjectUtils.isEmpty(updateUserAccountDTO.getPhone()) &&
                ObjectUtils.isEmpty(updateUserAccountDTO.getEmail())) {
            throw new BusinessException("参数异常");
        }
        CurrentUser currentUser = CurrentUserResolver.getCurrentUser(user);
        userDaoService.isUserExist(currentUser.getId());
        // 判断修改手机号或邮箱功能是否开启
        settingService.checkFunctionAndSetting(updateUserAccountDTO.getPhone(), updateUserAccountDTO.getEmail(), FunctionClassifyEnum.MODIFY_ACCOUNT);

        String account = !ObjectUtils.isEmpty(updateUserAccountDTO.getEmail()) ? updateUserAccountDTO.getEmail() : updateUserAccountDTO.getPhone();
        // 判断手机号或邮箱是否已经被注册
        UserDO dbUser = userDaoService.getUserByUsername(account);
        if (!ObjectUtils.isEmpty(dbUser)) {
            if (!ObjectUtils.isEmpty(updateUserAccountDTO.getPhone())) {
                throw new RepeatException(3, "电话号码重复");
            }
            if (!ObjectUtils.isEmpty(updateUserAccountDTO.getEmail())) {
                throw new RepeatException(4, "邮箱重复");
            }
            if (dbUser.getId().equals(currentUser.getId())) {
                throw new PreconditionFailedException(5, "手机号或邮箱不能与之前的相同");
            }
        }
        dbUser = userDaoService.getUserDetailById(currentUser.getId());
        // 判断密码是否正确
        BCryptPasswordEncoder bcryptPasswordEncoder = new BCryptPasswordEncoder();
        if (!bcryptPasswordEncoder.matches(updateUserAccountDTO.getPassword(), dbUser.getPassword())) {
            throw new BusinessException("密码错误");
        }
        // 校验验证码
        verifyCodeService.checkVerifyCode(account, FunctionClassifyEnum.MODIFY_ACCOUNT.getFunction(), updateUserAccountDTO.getVerifyCode());
        // 更新用户手机号或邮箱
        UserDO updateUser = new UserDO();
        BeanUtils.copyProperties(updateUserAccountDTO, updateUser);
        updateUser.setId(currentUser.getId());
        userDaoService.updateUserById(updateUser);
    }

    @Override
    public void updateUserPwd(String user, UpdateUserPwd updateUserPwd) {
        if (updateUserPwd.getOldPassword().equals(updateUserPwd.getNewPassword())) {
            throw new BusinessException("新密码不能与旧密码相同");
        }
        if (!updateUserPwd.getNewPassword().equals(updateUserPwd.getReNewPassword())) {
            throw new BusinessException("两次输入的新密码不一致");
        }
        CurrentUser currentUser = CurrentUserResolver.getCurrentUser(user);
        userDaoService.isUserExist(currentUser.getId());
        UserDO userDO = userDaoService.getUserDetailById(currentUser.getId());
        if (!userDO.getForcePassword()) {
            throw new PreconditionFailedException(4, "不满足修改密码条件");
        }
        BCryptPasswordEncoder bcryptPasswordEncoder = new BCryptPasswordEncoder();
        if (!bcryptPasswordEncoder.matches(updateUserPwd.getOldPassword(), userDO.getPassword())) {
            throw new PreconditionFailedException(3, "旧密码错误");
        }
        UserDO updateUser = new UserDO();
        updateUser.setId(userDO.getId());
        updateUser.setPassword(updateUserPwd.getNewPassword());
        updateUser.setForcePassword(false);
        userDaoService.updateUserById(updateUser);
    }
}